﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Concurrent;
using Net.Share;
using Net.System;
using Net.Event;
using Net.Config;

namespace Net.Server
{
    /// <summary>
    /// 网络玩家 - 当客户端连接服务器后都会为每个客户端生成一个网络玩家对象，(玩家对象由服务器管理) 2019.9.9
    /// <code>注意:不要试图new player出来, new出来后是没有作用的!</code>
    /// </summary>
    public partial class ClientPeer : ClientUnitBase
    {
        #region 字段
        internal Action<ClientPeer, IRpcModel, ISegment> receiveHandlerInternal;
        #endregion

        /// <summary>
        /// 服务器对等端配置
        /// </summary>
        public ServerConfig Config { get => (ServerConfig)(config ??= new ServerConfig()); set => config = value; }
        /// <summary>
        /// 异步事件回调对象
        /// </summary>
        public SocketAsyncEventArgs AsyncEventArgs { get; set; }
        private ThreadGroup group;
        /// <summary>
        /// 当前玩家所在的线程组对象
        /// </summary>
        public ThreadGroup Group
        {
            get => group;
            set
            {
                group?.Remove(this);
                group = value;
                group?.Add(this); //当释放后Group = null;
            }
        }
        /// <summary>
        /// 当前客户端请求的Token, 用于客户端响应, 如果在Rpc执行方法使用异步, 则需要记录一下token再异步, 否则token会被冲掉, 导致响应token错误
        /// </summary>
        public RequestToken Token { get; set; }
        /// <summary>
        /// 服务器对象
        /// </summary>
        public ServerBase Server { get; set; }
        /// <summary>
        /// 排队队列
        /// </summary>
        internal ConcurrentQueue<ClientPeer> QueueUp;
        /// <summary>
        /// 当前排队座号
        /// </summary>
        public int QueueUpNo { get; internal set; }
        /// <summary>
        /// 是否属于排队状态
        /// </summary>
        public bool IsQueueUp => QueueUpNo > 0;
        /// <summary>
        /// 断线处理倒计时
        /// </summary>
        public uint ReconnectTimeout { get; internal set; }

        #region 创建网络客户端(玩家)
        /// <summary>
        /// 构造网络客户端
        /// </summary>
        public ClientPeer() { }

        /// <summary>
        /// 构造网络客户端，Tcp
        /// </summary>
        /// <param name="client">客户端套接字</param>
        public ClientPeer(Socket client)
        {
            Client = client;
            RemotePoint = client.RemoteEndPoint;
        }

        /// <summary>
        /// 构造网络客户端
        /// </summary>
        /// <param name="remotePoint"></param>
        public ClientPeer(EndPoint remotePoint)
        {
            RemotePoint = remotePoint;
        }
        #endregion

        #region 客户端释放内存
        /// <summary>
        /// 析构网络客户端
        /// </summary>
        ~ClientPeer()
        {
            Dispose();
        }

        /// <summary>
        /// 释放
        /// </summary>
        public override void Dispose()
        {
            if (IsDispose)
                return;
            base.Dispose();
            AsyncEventArgs?.Dispose();
            Group = null;
        }
        #endregion

        public virtual void NetworkUpdate()
        {
            if (IsDispose)
                return;
            if (!CheckIsConnected())
                return;
            ReceiveHandler();
            SendDirect();
            NetworkLoop.FixedUpdate(16, false);
            OnNetworkTick();
        }

        private bool CheckIsQueueUp()
        {
            var isQueueUp = QueueUpNo > 0;
            if (isQueueUp)
            {
                var segment1 = BufferPool.Take(8);
                segment1.Write(QueueUp.Count);
                segment1.Write(QueueUpNo);
                Call(NetCmd.QueueUp, segment1.ToArray(true, true));
            }
            return isQueueUp;
        }

        protected override void OnCommand1Handler(IRpcModel model, ISegment segment)
        {
            if (CheckIsQueueUp())
                return;
            Token = new RequestToken(model.Token);
            switch (model.Cmd)
            {
                case NetCmd.IdentifyHash:
                    SetClientIdentity();//此处发的identity是连接时的标识, 还不是开发者自定义的标识
                    break;
                default:
                    base.OnCommand1Handler(model, segment);
                    break;
            }
        }

        /// <inheritdoc/>
        protected override void OnReceiveDataHandler(IRpcModel model, ISegment segment)
        {
            if (model.Protocol != 0 && model.Cmd == NetCmd.EntityRpcHash)
                OnRpcHandler(model);
            else
            {
                OnReceiveData?.Invoke(model);
                receiveHandlerInternal(this, model, segment);
            }
        }

        protected virtual bool CheckIsConnected()
        {
            if (!Connected)
            {
                var tick = (uint)Environment.TickCount;
                if (tick >= ReconnectTimeout)
                    RemoveClient();
                return false;
            }
            if (!Client.Connected)
            {
                ConnectLost(SocketError.NotConnected);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 检查心跳
        /// </summary>
        protected override bool KeepAliveHandler()
        {
            if (Environment.TickCount > KeepAliveTimeout)
            {
                Call(NetCmd.KeepAlive, new byte[1]);
                if (KeepAliveCount++ >= Config.KeepAliveCount) //udp协议如果断开是不知道的, 也不会发出异常, 所以如果在保活期内没有回应则说明断线了
                {
                    ConnectLost(SocketError.NotConnected);
                }
            }
            return true;
        }

        public void SetClientIdentity()
        {
            var segment = BufferPool.Take(byte.MaxValue);
            segment.Write(UserId);
            var adapterType = string.Empty;
            if (SerializeAdapter != null)
                adapterType = SerializeAdapter.GetType().ToString();
            segment.Write(adapterType);
            var versionPacked = 0;
            versionPacked |= (config.Version.Major & 0xFF) << 24;
            versionPacked |= (config.Version.Minor & 0xFF) << 16;
            versionPacked |= (config.Version.Build & 0xFF) << 8;
            versionPacked |= config.Version.Revision & 0x0FF;
            segment.Write(versionPacked);
            Call(NetCmd.Identify, segment.ToArray(true));
        }

        public override void ConnectLost(SocketError socketError)
        {
            Connected = false;
            ReconnectTimeout = (uint)Environment.TickCount + Config.ReconnectionTimeout;
            OnConnectLost();
        }

        /// <summary>
        /// 从客户端字典中移除客户端
        /// </summary>
        public virtual void RemoveClient() => Close();

        /// <summary>
        /// 关闭此客户端连接
        /// </summary>
        public override void Close()
        {
            Call(NetCmd.Disconnect, new byte[1]);
            SendDirect();
            Server.RemoveClient(RemotePoint);
        }

        /// <summary>
        /// 当客户端连接中断, 此时还会等待客户端重连, 如果10秒后没有重连上来就会真的断开
        /// </summary>
        public virtual void OnConnectLost() => NDebug.Log($"[{this}]连接中断!");

        /// <summary>
        /// 当断线重连成功触发
        /// </summary>
        public virtual void OnReconnecting() => NDebug.Log($"[{this}]重连成功!");

        /// <summary>
        /// 当服务器判定客户端为断线或连接异常时，移除客户端时调用
        /// </summary>
        public virtual void OnRemoveClient() { }

        /// <summary>
        /// 检查send方法的发送队列是否已到达极限, 到达极限则不允许新的数据放入发送队列, 需要等待队列消耗后才能放入新的发送数据
        /// </summary>
        /// <returns>是否可发送数据</returns>
        public bool CheckCall()
        {
            return RpcQueue.Count < Server.Config.LimitQueueCount;
        }

        /// <inheritdoc/>
        public override string ToString()
        {
            return $"用户ID:{UserId} IP:{RemotePoint}";
        }
    }
}